package com.ruoyi.common.utils.websocket;

import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import lombok.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author huangziliang
 * @version 1.0
 * @date 2021/4/11 2:23
 */
public class WebSocketUtils {

    private static final Logger LOG = LoggerFactory.getLogger(WebSocketUtils.class);

    private static final Map<String, WebSocketClient> NAME_MAP = new ConcurrentHashMap<>();

    private static final Map<Session, WebSocketClient> SESSION_MAP = new ConcurrentHashMap<>();

    public static void openSession(WebSocketClient client) {
        setClient(client);
    }

    public static void openSession(Session session, String userName, LoginUser loginUser) {
        WebSocketClient client = new WebSocketClient(session, userName, loginUser);
        client.setUri(session.getRequestURI().toString());
        setClient(client);
    }

    public static void closeSession(Session session) {
        if (SESSION_MAP.containsKey(session)) {
            WebSocketClient client = SESSION_MAP.get(session);
            NAME_MAP.remove(client.token);
            SESSION_MAP.remove(session);
        }
    }

    @SneakyThrows
    public static void exitSession(Session session) {
        if (session.isOpen()) {
            session.close();
        }
    }

    public static WebSocketClient getClient(String token) {
        return NAME_MAP.get(token);
    }

    public static WebSocketClient getClient(Session session) {
        return SESSION_MAP.get(session);
    }

    public static LoginUser getLoginUser(Session session) {
        if (SESSION_MAP.containsKey(session)){
            return SESSION_MAP.get(session).loginUser;
        }
        return null;
    }


    public static void setClient(WebSocketClient client) {
        if (NAME_MAP.containsKey(client.token)) {
            WebSocketClient oldClient = NAME_MAP.get(client.token);
            if (!SESSION_MAP.containsKey(client.session)) {
                SESSION_MAP.remove(oldClient.session);
                SESSION_MAP.put(client.session, client);
            }
        } else {
            SESSION_MAP.put(client.session, client);
        }
        NAME_MAP.put(client.token, client);
    }

    public static void sendMessage(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            WebSocketClient client = SESSION_MAP.get(session);
            if (client == null) {
                LOG.error(String.format("发送消息(%s)出错！", message), e);
            } else {
                LOG.error(String.format("向用户：%s，发送消息(%s)出错！", client.token, message), e);
            }
        }
    }

    public static void sendSuccessMsg(Session session, String msg){
        sendMessage(session, AjaxResult.success(msg));
    }

    public static void sendErrorMsg(Session session, String msg){
        sendMessage(session, AjaxResult.error(msg));
    }

    public static void sendMessage(Session session, Object message) {
        sendMessage(session, JSON.toJSONString(message));
    }

    /**
     * 向指定客户端发送消息
     *
     * @param token
     * @param message
     */
    public static void sendMessage(String token, String message) {
        WebSocketClient client = NAME_MAP.get(token);
        if (client != null) {
            sendMessage(client.session, message);
        } else {
            LOG.error(String.format("用户%s不存在，无法发送消息！", token));
        }
    }

    public static int getNum() {
        return Math.min(SESSION_MAP.size(), NAME_MAP.size());
    }

    @Getter
    @Setter
    @NoArgsConstructor()
    @AllArgsConstructor
    public static class WebSocketClient {
        /**
         * 与某个客户端的连接会话，需要通过它来给客户端发送数据
         */
        private Session session;

        /**
         * 连接的uri
         */
        private String uri;

        private String token;

        private LoginUser loginUser;

        public WebSocketClient(Session session, String token, LoginUser loginUser) {
            this.session = session;
            this.token = token;
            this.loginUser = loginUser;
        }
    }

}
